package org.superheroes.androidclient.net;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.util.StringTokenizer;

import org.superheroes.androidclient.enums.State;
import org.superheroes.androidclient.packets.RTPPacket;

import android.graphics.Bitmap;
import android.util.Log;

/**
 * 
 * @author Jeffrey Goudzwaard
 * @author Davin Terrizzi
 * @version 1.0
 *
 */
public class Client implements Runnable
{
	private static final int	PORT			= 25000;

	private static final String	LOG_TAG			= Client.class.getName();

	private static final String	HOST			= "77.169.237.70";

	private static Client		client;

	private BufferedReader		reader;

	private BufferedWriter		writer;

	private DatagramPacket		dataPacket;

	private DatagramSocket		udpSocket;

	private byte[]				buffer;

	private State				state;

	private Socket				tcpSocket;

	private int					seqnr			= 0;
	private int					sessionID		= 0;
	private int					replyCode;

	private String				videoFileName	= "/movies/movie.Mjpeg";

	private RTPPacket			rtpPacket;

	private Bitmap				bmp;

	private Client()
	{
		init();
	}

	/**
	 * Returns the client instance, a way for using Singletons
	 * @return Client
	 */
	public static Client getCLient()
	{
		if ( client == null )
		{
			client = new Client();
		}

		return client;
	}

	private void init()
	{
		buffer = new byte[ 15000 ];
	}

	public Bitmap getBitmap()
	{
		return bmp;
	}

	public void setBitmap( final Bitmap bmp )
	{
		this.bmp = bmp;
	}

	public RTPPacket getRTPPacket()
	{
		return rtpPacket;
	}

	public void setRTPPacket( RTPPacket packet )
	{
		this.rtpPacket = packet;
	}

	/**
	 * Starts the server
	 * Makes a new connection to the server
	 * Created the writer and reader for data exchange
	 */
	public void start()
	{
		try
		{
			tcpSocket = new Socket( InetAddress.getByName( HOST ), 3030 );

			reader = new BufferedReader( new InputStreamReader( tcpSocket.getInputStream() ) );
			writer = new BufferedWriter( new OutputStreamWriter( tcpSocket.getOutputStream() ) );

			state = State.INIT;
		}
		catch ( Exception e )
		{
			Log.e( LOG_TAG, e.toString() );
		}
	}

	public void setDatagramPacket( DatagramPacket packet )
	{
		this.dataPacket = packet;
	}

	/**
	 * Sends a request to the server
	 * @param requestType
	 */
	public void sendRequest( State requestType )
	{
		try
		{
			switch ( requestType )
			{
				case SETUP:
					writer.write( "SETUP " + videoFileName + " RTSP/1.0" + "\r\n" );
					writer.write( "CSeq: " + seqnr + "\r\n" );
					writer.write( "Transport: RTP/UDP; client_port= " + PORT + "\r\n" );
					break;
				case PAUSE:
					writer.write( "PAUSE " + videoFileName + " RTSP/1.0 \r\n" );
					writer.write( "CSeq: " + seqnr + "\r\n" );
					writer.write( "Session: " + sessionID + "\r\n" );
					break;
				case PLAY:
					writer.write( "PLAY " + videoFileName + " RTSP/1.0\r\n" );
					writer.write( "CSeq: " + seqnr + "\r\n" );
					writer.write( "Session: " + sessionID + "\r\n" );
					break;
				case TEARDOWN:
					writer.write( "TEARDOWN " + videoFileName + " RTSP/1.0\r\n" );
					writer.write( "CSeq: " + seqnr + "\r\n" );
					writer.write( "Session: " + sessionID + "\r\n" );
					break;
				default:
					Log.i( LOG_TAG, "Not handling requestType: " + requestType );

			}
		}
		catch ( IOException e )
		{
			Log.e( LOG_TAG, e.toString() );
		}
		finally
		{
			try
			{
				if ( writer != null )
				{
					writer.flush();
				}
			}
			catch ( IOException e )
			{
				Log.e( LOG_TAG, e.toString() );
			}
		}
	}

	/**
	 * Parses the Server response
	 * @return
	 */
	public int parseServerResponse()
	{
		Thread t = new Thread( new Runnable()
		{
			@Override
			public void run()
			{
				try
				{
					String stateLine = reader.readLine();

					StringTokenizer tokens = new StringTokenizer( stateLine );
					tokens.nextToken();

					replyCode = Integer.parseInt( tokens.nextToken() );

					Client.getCLient().setReplyCode( replyCode );

					if ( replyCode == 200 )
					{
						String seqNumLine = reader.readLine();
						Log.i( LOG_TAG, "Seq: " + seqNumLine );

						String sessionLine = reader.readLine();
						Log.i( LOG_TAG, "session: " + sessionLine );

						tokens = new StringTokenizer( sessionLine );
						tokens.nextToken();
						sessionID = Integer.parseInt( tokens.nextToken() );

					}
				}
				catch ( NumberFormatException ex )
				{
					Log.e( LOG_TAG, ex.toString() );
				}
				catch ( IOException e )
				{
					Log.e( LOG_TAG, e.toString() );
				}
			}

		} );
		t.start();

		try
		{
			Thread.sleep( 100 );
		}
		catch ( InterruptedException e )
		{
			Log.e( LOG_TAG, e.toString() );
		}

		return replyCode;
	}

	public void setReplyCode( int replyCode )
	{
		this.replyCode = replyCode;
	}

	public State getState()
	{
		return state;
	}

	public void setUDPSocket( DatagramSocket udpSocket )
	{
		this.udpSocket = udpSocket;
	}

	public int getPort()
	{
		return PORT;
	}

	public DatagramSocket getUDPSocket()
	{
		return udpSocket;
	}

	public void setSeqnr( int seqNr )
	{
		this.seqnr = seqNr;
	}

	public void setState( State state )
	{
		this.state = state;
	}

	@Override
	public void run()
	{
		start();
	}

	public byte[] getBuffer()
	{
		return buffer;
	}

	public DatagramPacket getDataGramPacket()
	{
		return dataPacket;
	}
}
